JBoss Community Archive (Read Only)

JBoss AS 7.2

SOAP over JMS

JBoss Web Services allows communication over the JMS transport. The functionality comes from Apache CXF support for the SOAP over Java Message Service 1.0 specification, which is aimed at a set of standards for interoperable transport of SOAP messages over JMS.

On top of Apache CXF functionalities, the JBossWS integration allows users to deploy WS archives containing both JMS and HTTP endpoints the same way as they do for basic HTTP WS endpoints (in war archives). The webservices layer of JBoss Application Server takes care of looking for JMS enpdoints in the deployed archive and starts them delegating to the Apache CXF core similarly as with HTTP endpoints.

Configuring SOAP over JMS

As per specification, the SOAP over JMS transport configuration is controlled by proper elements and attributes in the binding and service elements of the WSDL contract. So a JMS endpoint is usually developed using a contract-first approach.

The Apache CXF documentation covers all the details of the supported configurations. The minimum configuration implies:

  • setting a proper JMS URI in the soap:address location [1]

  • providing a JNDI connection factory name to be used for connecting to the queues [2]

  • setting the transport binding [3]

<wsdl:definitions name="HelloWorldService" targetNamespace="http://org.jboss.ws/jaxws/cxf/jms"
  xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
  xmlns:tns="http://org.jboss.ws/jaxws/cxf/jms"
  xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
  xmlns:soapjms="http://www.w3.org/2010/soapjms/"
  xmlns:xsd="http://www.w3.org/2001/XMLSchema">
...

<wsdl:binding name="HelloWorldServiceSoapBinding" type="tns:HelloWorld">
  <soap:binding style="document" transport="http://www.w3.org/2010/soapjms/"/> <!-- 3 -->
  <wsdl:operation name="echo">
    <soap:operation soapAction="" style="document"/>
    <wsdl:input name="echo">
      <soap:body use="literal"/>
    </wsdl:input>
    <wsdl:output name="echoResponse">
      <soap:body use="literal"/>
    </wsdl:output>
  </wsdl:operation>
</wsdl:binding>
<wsdl:service name="HelloWorldService">
  <soapjms:jndiConnectionFactoryName>java:/ConnectionFactory</soapjms:jndiConnectionFactoryName> <!-- 2 -->
  <wsdl:port binding="tns:HelloWorldServiceSoapBinding" name="HelloWorldImplPort">
    <soap:address location="jms:queue:testQueue"/> <!-- 1 -->
  </wsdl:port>
</wsdl:service>

Apache CXF takes care of setting up the JMS transport for endpoint implementations whose @WebService annotation points to a port declared for JMS transport as explained above.

JBossWS currently supports POJO endpoints only for JMS transport use. The endpoint classes can be deployed as part of jar or war archives.

The web.xml descriptor in war archives doesn't need any entry for JMS endpoints.

At the time of writing, the Apache CXF support for JMS transport requires Spring libraries to be available at runtime.

Please make sure Spring is properly installed on the application server, perhaps using the JBossWS installation option for it.

Examples

JMS endpoint only deployment

In this example we create a simple endpoint relying on SOAP over JMS and deploy it as part of a jar archive.

The endpoint is created using wsconsume tool from a WSDL contract such as:

<?xml version='1.0' encoding='UTF-8'?>
<wsdl:definitions name="HelloWorldService" targetNamespace="http://org.jboss.ws/jaxws/cxf/jms"
  xmlns:ns1="http://schemas.xmlsoap.org/soap/http"
  xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
  xmlns:tns="http://org.jboss.ws/jaxws/cxf/jms"
  xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
  xmlns:soapjms="http://www.w3.org/2010/soapjms/"
  xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <wsdl:types>
<xs:schema elementFormDefault="unqualified" targetNamespace="http://org.jboss.ws/jaxws/cxf/jms" version="1.0" xmlns:tns="http://org.jboss.ws/jaxws/cxf/jms" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="echo" type="tns:echo"/>
<xs:element name="echoResponse" type="tns:echoResponse"/>
<xs:complexType name="echo">
    <xs:sequence>
      <xs:element minOccurs="0" name="arg0" type="xs:string"/>
    </xs:sequence>
  </xs:complexType>
<xs:complexType name="echoResponse">
    <xs:sequence>
      <xs:element minOccurs="0" name="return" type="xs:string"/>
    </xs:sequence>
  </xs:complexType>
</xs:schema>
  </wsdl:types>
  <wsdl:message name="echoResponse">
    <wsdl:part element="tns:echoResponse" name="parameters">
    </wsdl:part>
  </wsdl:message>
  <wsdl:message name="echo">
    <wsdl:part element="tns:echo" name="parameters">
    </wsdl:part>
  </wsdl:message>
  <wsdl:portType name="HelloWorld">
    <wsdl:operation name="echo">
      <wsdl:input message="tns:echo" name="echo">
    </wsdl:input>
      <wsdl:output message="tns:echoResponse" name="echoResponse">
    </wsdl:output>
    </wsdl:operation>
  </wsdl:portType>
  <wsdl:binding name="HelloWorldServiceSoapBinding" type="tns:HelloWorld">
    <soap:binding style="document" transport="http://www.w3.org/2010/soapjms/"/>
    <wsdl:operation name="echo">
      <soap:operation soapAction="" style="document"/>
      <wsdl:input name="echo">
        <soap:body use="literal"/>
      </wsdl:input>
      <wsdl:output name="echoResponse">
        <soap:body use="literal"/>
      </wsdl:output>
    </wsdl:operation>
  </wsdl:binding>
  <wsdl:service name="HelloWorldService">
    <soapjms:jndiConnectionFactoryName>java:jms/RemoteConnectionFactory</soapjms:jndiConnectionFactoryName>
    <soapjms:jndiInitialContextFactory>org.jboss.naming.remote.client.InitialContextFactory</soapjms:jndiInitialContextFactory>
    <soapjms:jndiURL>remote://myhost:4447</soapjms:jndiURL>
    <wsdl:port binding="tns:HelloWorldServiceSoapBinding" name="HelloWorldImplPort">
      <soap:address location="jms:queue:testQueue"/>
    </wsdl:port>
  </wsdl:service>
  <wsdl:service name="HelloWorldServiceLocal">
    <soapjms:jndiConnectionFactoryName>java:/ConnectionFactory</soapjms:jndiConnectionFactoryName>
    <wsdl:port binding="tns:HelloWorldServiceSoapBinding" name="HelloWorldImplPort">
      <soap:address location="jms:queue:testQueue"/>
    </wsdl:port>
  </wsdl:service>
</wsdl:definitions>

The HelloWorldImplPort here is meant for using the testQueue that's available by default on JBoss Application Server 7

At the time of writing, java:/ConnectionFactory is the default connection factory JNDI location on JBoss Application Server 7

For allowing remote JNDI lookup of the connection factory, a specific service (HelloWorldService) for remote clients is added to the WSDL. The java:jms/RemoteConnectionFactory is the JNDI location of the same connection factory mentioned above, except it's exposed for remote lookup. The soapjms:jndiInitialContextFactory and soap:jmsjndiURL complete the remote connection configuration, specifying the initial context factory class to use and the JNDI registry address.

Have a look at the application server domain for finding out the configured connection factory JNDI locations.

Remote JNDI support is available starting from JBoss Application Server 7.1.

The endpoint implementation is a basic JAX-WS POJO using @WebService annotation to refer to the consumed contract:

package org.jboss.test.ws.jaxws.cxf.jms;

import javax.jws.WebService;

@WebService
(
   portName = "HelloWorldImplPort",
   serviceName = "HelloWorldServiceLocal",
   wsdlLocation = "META-INF/wsdl/HelloWorldService.wsdl",
   endpointInterface = "org.jboss.test.ws.jaxws.cxf.jms.HelloWorld",
   targetNamespace = "http://org.jboss.ws/jaxws/cxf/jms"
)
public class HelloWorldImpl implements HelloWorld
{
   public String echo(String input)
   {
      return input;
   }
}

The endpoint implementation references the HelloWorldServiceLocal wsdl service, so that the local JNDI connection factory location is used for starting the endpoint on server side.

That's pretty much all. We just need to package the generated service endpoint interface, the endpoint implementation and the WSDL file in a jar archive and deploy it:

alessio@inuyasha /dati/jbossws/stack/cxf/trunk $ jar -tvf ./modules/testsuite/cxf-spring-tests/target/test-libs/jaxws-cxf-jms-only-deployment.jar
     0 Thu Jun 23 15:18:44 CEST 2011 META-INF/
   129 Thu Jun 23 15:18:42 CEST 2011 META-INF/MANIFEST.MF
     0 Thu Jun 23 15:18:42 CEST 2011 org/
     0 Thu Jun 23 15:18:42 CEST 2011 org/jboss/
     0 Thu Jun 23 15:18:42 CEST 2011 org/jboss/test/
     0 Thu Jun 23 15:18:42 CEST 2011 org/jboss/test/ws/
     0 Thu Jun 23 15:18:42 CEST 2011 org/jboss/test/ws/jaxws/
     0 Thu Jun 23 15:18:42 CEST 2011 org/jboss/test/ws/jaxws/cxf/
     0 Thu Jun 23 15:18:42 CEST 2011 org/jboss/test/ws/jaxws/cxf/jms/
   313 Thu Jun 23 15:18:42 CEST 2011 org/jboss/test/ws/jaxws/cxf/jms/HelloWorld.class
  1173 Thu Jun 23 15:18:42 CEST 2011 org/jboss/test/ws/jaxws/cxf/jms/HelloWorldImpl.class
     0 Thu Jun 23 15:18:40 CEST 2011 META-INF/wsdl/
  3074 Thu Jun 23 15:18:40 CEST 2011 META-INF/wsdl/HelloWorldService.wsdl

A dependency on org.hornetq module needs to be added in MANIFEST.MF when deploying to JBoss Application Server 7.

Manifest-Version: 1.0

Ant-Version: Apache Ant 1.7.1

Created-By: 17.0-b16 (Sun Microsystems Inc.)

Dependencies: org.hornetq

A JAX-WS client can interact with the JMS endpoint the usual way:

URL wsdlUrl = ...
//start another bus to avoid affecting the one that could already be assigned to the current thread - optional but highly suggested
Bus bus = BusFactory.newInstance().createBus();
BusFactory.setThreadDefaultBus(bus);
try
{
   QName serviceName = new QName("http://org.jboss.ws/jaxws/cxf/jms", "HelloWorldService");
   Service service = Service.create(wsdlUrl, serviceName);
   HelloWorld proxy = (HelloWorld) service.getPort(new QName("http://org.jboss.ws/jaxws/cxf/jms", "HelloWorldImplPort"), HelloWorld.class);
   setupProxy(proxy);
   proxy.echo("Hi");
}
finally
{
   bus.shutdown(true);
}

The WSDL location URL needs to be retrieved in a custom way, depending on the client application. Given the endpoint is JMS only, there's no automatically published WSDL contract.

in order for performing the remote invocation (which internally goes through remote JNDI lookup of the connection factory), the calling user credentials need to be set into the Apache CXF JMSConduit:

private void setupProxy(HelloWorld proxy) {
   JMSConduit conduit = (JMSConduit)ClientProxy.getClient(proxy).getConduit();
   JNDIConfiguration jndiConfig = conduit.getJmsConfig().getJndiConfig();
   jndiConfig.setConnectionUserName("user");
   jndiConfig.setConnectionPassword("password");
   Properties props = conduit.getJmsConfig().getJndiTemplate().getEnvironment();
   props.put(Context.SECURITY_PRINCIPAL, "user");
   props.put(Context.SECURITY_CREDENTIALS, "password");
}

Have a look at the JBoss Application Server 7 domain and messaging configuration for finding out the actual security requirements. At the time of writing, a user with guest role is required and that's internally checked using the other security domain.

Of course once the endpoint is exposed over JMS transport, any plain JMS client can also be used to send messages to the webservice endpoint. You can have a look at the SOAP over JMS spec details and code the client similarly to

Properties env = new Properties();
env.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.remote.client.InitialContextFactory");
env.put(Context.PROVIDER_URL, "remote://myhost:4447");
env.put(Context.SECURITY_PRINCIPAL, "user");
env.put(Context.SECURITY_CREDENTIALS, "password");
InitialContext context = new InitialContext(env);
QueueConnectionFactory connectionFactory = (QueueConnectionFactory)context.lookup("jms/RemoteConnectionFactory");
Queue reqQueue = (Queue)context.lookup("jms/queue/test");
Queue resQueue = (Queue)context.lookup("jms/queue/test");
QueueConnection con = connectionFactory.createQueueConnection("user", "password");
QueueSession session = con.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
QueueReceiver receiver = session.createReceiver(resQueue);
ResponseListener responseListener = new ResponseListener(); //a custom response listener...
receiver.setMessageListener(responseListener);
con.start();
TextMessage message = session.createTextMessage(reqMessage);
message.setJMSReplyTo(resQueue);

//setup SOAP-over-JMS properties...
message.setStringProperty("SOAPJMS_contentType", "text/xml");
message.setStringProperty("SOAPJMS_requestURI", "jms:queue:testQueue");

QueueSender sender = session.createSender(reqQueue);
sender.send(message);
sender.close();

...

JMS and HTTP endpoints deployment

In this example we create a deployment containing an endpoint that serves over both HTTP and JMS transports.

We from a WSDL contract such as below (please note we've two binding / portType for the same service):

<?xml version='1.0' encoding='UTF-8'?>
<wsdl:definitions name="HelloWorldService" targetNamespace="http://org.jboss.ws/jaxws/cxf/jms"
  xmlns:ns1="http://schemas.xmlsoap.org/soap/http"
  xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
  xmlns:tns="http://org.jboss.ws/jaxws/cxf/jms"
  xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
  xmlns:soapjms="http://www.w3.org/2010/soapjms/"
  xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <wsdl:types>
<xs:schema elementFormDefault="unqualified" targetNamespace="http://org.jboss.ws/jaxws/cxf/jms" version="1.0"
  xmlns:tns="http://org.jboss.ws/jaxws/cxf/jms" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="echo" type="tns:echo"/>
<xs:element name="echoResponse" type="tns:echoResponse"/>
<xs:complexType name="echo">
    <xs:sequence>
      <xs:element minOccurs="0" name="arg0" type="xs:string"/>
    </xs:sequence>
  </xs:complexType>
<xs:complexType name="echoResponse">
    <xs:sequence>
      <xs:element minOccurs="0" name="return" type="xs:string"/>
    </xs:sequence>
  </xs:complexType>
</xs:schema>
  </wsdl:types>
  <wsdl:message name="echoResponse">
    <wsdl:part element="tns:echoResponse" name="parameters">
    </wsdl:part>
  </wsdl:message>
  <wsdl:message name="echo">
    <wsdl:part element="tns:echo" name="parameters">
    </wsdl:part>
  </wsdl:message>
  <wsdl:portType name="HelloWorld">
    <wsdl:operation name="echo">
      <wsdl:input message="tns:echo" name="echo">
    </wsdl:input>
      <wsdl:output message="tns:echoResponse" name="echoResponse">
    </wsdl:output>
    </wsdl:operation>
  </wsdl:portType>
  <wsdl:binding name="HelloWorldServiceSoapBinding" type="tns:HelloWorld">
    <soap:binding style="document" transport="http://www.w3.org/2010/soapjms/"/>
    <wsdl:operation name="echo">
      <soap:operation soapAction="" style="document"/>
      <wsdl:input name="echo">
        <soap:body use="literal"/>
      </wsdl:input>
      <wsdl:output name="echoResponse">
        <soap:body use="literal"/>
      </wsdl:output>
    </wsdl:operation>
  </wsdl:binding>
  <wsdl:binding name="HttpHelloWorldServiceSoapBinding" type="tns:HelloWorld">
    <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
    <wsdl:operation name="echo">
      <soap:operation soapAction="" style="document"/>
      <wsdl:input name="echo">
        <soap:body use="literal"/>
      </wsdl:input>
      <wsdl:output name="echoResponse">
        <soap:body use="literal"/>
      </wsdl:output>
    </wsdl:operation>
  </wsdl:binding>
  <wsdl:service name="HelloWorldService">
    <soapjms:jndiConnectionFactoryName>java:jms/RemoteConnectionFactory</soapjms:jndiConnectionFactoryName>
    <soapjms:jndiInitialContextFactory>org.jboss.naming.remote.client.InitialContextFactory</soapjms:jndiInitialContextFactory>
    <soapjms:jndiURL>remote://localhost:4447</soapjms:jndiURL>
    <wsdl:port binding="tns:HelloWorldServiceSoapBinding" name="HelloWorldImplPort">
      <soap:address location="jms:queue:testQueue"/>
    </wsdl:port>
    <wsdl:port binding="tns:HttpHelloWorldServiceSoapBinding" name="HttpHelloWorldImplPort">
      <soap:address location="http://localhost:8080/jaxws-cxf-jms-http-deployment"/>
    </wsdl:port>
  </wsdl:service>
  <wsdl:service name="HelloWorldServiceLocal">
    <soapjms:jndiConnectionFactoryName>java:/ConnectionFactory</soapjms:jndiConnectionFactoryName>
    <wsdl:port binding="tns:HelloWorldServiceSoapBinding" name="HelloWorldImplPort">
      <soap:address location="jms:queue:testQueue"/>
    </wsdl:port>
</wsdl:definitions>

The same considerations of the previous example regarding the JMS queue and JNDI connection factory still apply.
Here we can implement the endpoint in multiple ways, either with a common implementation class that's extended by the JMS and HTTP ones, or keep the two implementation classes independent and just have them implement the same service endpoint interface:

package org.jboss.test.ws.jaxws.cxf.jms_http;

import javax.jws.WebService;

@WebService
(
   portName = "HelloWorldImplPort",
   serviceName = "HelloWorldServiceLocal",
   wsdlLocation = "WEB-INF/wsdl/HelloWorldService.wsdl",
   endpointInterface = "org.jboss.test.ws.jaxws.cxf.jms_http.HelloWorld",
   targetNamespace = "http://org.jboss.ws/jaxws/cxf/jms"
)
public class HelloWorldImpl implements HelloWorld
{
   public String echo(String input)
   {
      System.out.println("input: " + input);
      return input;
   }
}
package org.jboss.test.ws.jaxws.cxf.jms_http;

import javax.jws.WebService;

@WebService
(
   portName = "HttpHelloWorldImplPort",
   serviceName = "HelloWorldService",
   wsdlLocation = "WEB-INF/wsdl/HelloWorldService.wsdl",
   endpointInterface = "org.jboss.test.ws.jaxws.cxf.jms_http.HelloWorld",
   targetNamespace = "http://org.jboss.ws/jaxws/cxf/jms"
)
public class HttpHelloWorldImpl implements HelloWorld
{
   public String echo(String input)
   {
      System.out.println("input (http): " + input);
      return "(http) " + input;
   }
}

Both classes are packaged together the service endpoint interface and the WSDL file in a war archive:

alessio@inuyasha /dati/jbossws/stack/cxf/trunk $ jar -tvf ./modules/testsuite/cxf-spring-tests/target/test-libs/jaxws-cxf-jms-http-deployment.war
     0 Thu Jun 23 15:18:44 CEST 2011 META-INF/
   129 Thu Jun 23 15:18:42 CEST 2011 META-INF/MANIFEST.MF
     0 Thu Jun 23 15:18:44 CEST 2011 WEB-INF/
   569 Thu Jun 23 15:18:40 CEST 2011 WEB-INF/web.xml
     0 Thu Jun 23 15:18:44 CEST 2011 WEB-INF/classes/
     0 Thu Jun 23 15:18:42 CEST 2011 WEB-INF/classes/org/
     0 Thu Jun 23 15:18:42 CEST 2011 WEB-INF/classes/org/jboss/
     0 Thu Jun 23 15:18:42 CEST 2011 WEB-INF/classes/org/jboss/test/
     0 Thu Jun 23 15:18:42 CEST 2011 WEB-INF/classes/org/jboss/test/ws/
     0 Thu Jun 23 15:18:42 CEST 2011 WEB-INF/classes/org/jboss/test/ws/jaxws/
     0 Thu Jun 23 15:18:42 CEST 2011 WEB-INF/classes/org/jboss/test/ws/jaxws/cxf/
     0 Thu Jun 23 15:18:42 CEST 2011 WEB-INF/classes/org/jboss/test/ws/jaxws/cxf/jms_http/
   318 Thu Jun 23 15:18:42 CEST 2011 WEB-INF/classes/org/jboss/test/ws/jaxws/cxf/jms_http/HelloWorld.class
  1192 Thu Jun 23 15:18:42 CEST 2011 WEB-INF/classes/org/jboss/test/ws/jaxws/cxf/jms_http/HelloWorldImpl.class
  1246 Thu Jun 23 15:18:42 CEST 2011 WEB-INF/classes/org/jboss/test/ws/jaxws/cxf/jms_http/HttpHelloWorldImpl.class
     0 Thu Jun 23 15:18:40 CEST 2011 WEB-INF/wsdl/
  3068 Thu Jun 23 15:18:40 CEST 2011 WEB-INF/wsdl/HelloWorldService.wsdl

A trivial web.xml descriptor is also included to trigger the HTTP endpoint publish:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
  version="2.4">
  <servlet>
    <servlet-name>EndpointServlet</servlet-name>
    <servlet-class>org.jboss.test.ws.jaxws.cxf.jms_http.HttpHelloWorldImpl</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>EndpointServlet</servlet-name>
    <url-pattern>/*</url-pattern>
  </servlet-mapping>
</web-app>

Here too the MANIFEST.MF needs to declare a dependency on org.hornetq module when deploying to JBoss Application Server 7.

Finally, the JAX-WS client can ineract with both JMS and HTTP endpoints as usual:

//start another bus to avoid affecting the one that could already be assigned to current thread - optional but highly suggested
Bus bus = BusFactory.newInstance().createBus();
BusFactory.setThreadDefaultBus(bus);
try
{
   QName serviceName = new QName("http://org.jboss.ws/jaxws/cxf/jms", "HelloWorldService");
   Service service = Service.create(wsdlUrl, serviceName);

   //JMS test
   HelloWorld proxy = (HelloWorld) service.getPort(new QName("http://org.jboss.ws/jaxws/cxf/jms", "HelloWorldImplPort"), HelloWorld.class);
   setupProxy(proxy);
   proxy.echo("Hi");
   //HTTP test
   HelloWorld httpProxy = (HelloWorld) service.getPort(new QName("http://org.jboss.ws/jaxws/cxf/jms", "HttpHelloWorldImplPort"), HelloWorld.class);
   httpProxy.echo("Hi");
}
finally
{
   bus.shutdown(true);
}

Use of Endpoint.publish() API

An alternative to deploying an archive containing JMS endpoints is in starting them directly using the JAX-WS Endpoint.publish(..) API.

That's as easy as doing:

Object implementor = new HelloWorldImpl();
Endpoint ep = Endpoint.publish("jms:queue:testQueue", implementor);
try
{
   //use or let others use the endpoint
}
finally
{
   ep.stop();
}

where HelloWorldImpl is a POJO endpoint implementation referencing a JMS port in a given WSDL contract, as explained in the previous examples.

The main difference among the deployment approach is in the direct control and responsibility over the endpoint lifecycle (start/publish and stop).

JBoss.org Content Archive (Read Only), exported from JBoss Community Documentation Editor at 2020-03-13 13:28:30 UTC, last content change 2012-03-09 14:28:16 UTC.